Namespace.Use("UI.Object", function(ns) {
    //
    //  Imports.
    //
    var Inherit = Namespace.Import("Base.Inherit");

    //
    //  Global.
    //
    var cst_InitialX = 0, cst_InitialY = 0;
    var cst_InitialWidth = 100, cst_InitialHeight = 100;
    var cst_PixelRatio = (function () {
        var ctx = document.createElement("canvas").getContext("2d"),
            dpr = window.devicePixelRatio || 1,
            bsr = ctx.webkitBackingStorePixelRatio ||
                  ctx.mozBackingStorePixelRatio ||
                  ctx.msBackingStorePixelRatio ||
                  ctx.oBackingStorePixelRatio ||
                  ctx.backingStorePixelRatio || 1;
        return dpr / bsr;
    })();
    var cst_InitialVisibility = true;
    var cst_InitialEnabled = true;
    var cst_InitialLayer = 0;
    var cst_StateUp = 0;
    var cst_StateOver = 1;
    var cst_StateDown = 2;

    //
    //  Classes.
    //
    var CanvasButton = Polymorphic.Create(
        [Function],
        function(renderer) {
            //  Let parent class initialize.
            ns.BaseObject.call(this);

            //
            //  Members.
            //
            var last_renderer = null;
            var last_x = -1, last_y = -1;
            var x = cst_InitialX, y = cst_InitialY;
            var last_state = -1;
            var state = cst_StateUp;
            var last_width = -1, last_height = -1;
            var width = cst_InitialWidth, height = cst_InitialHeight;
            var last_visibility = !cst_InitialVisibility;
            var visibility = cst_InitialVisibility;
            var last_enabled = !cst_InitialEnabled;
            var enabled = cst_InitialEnabled;
            var last_layer = -1;
            var layer = cst_InitialLayer;
            var element = $("<canvas>").css({
                "position": "absolute",
                "cursor": "pointer",
                "background-color": "transparent"
            });
            var context = element[0].getContext("2d");

            //  Set the graphic transformation parameters.
            context.setTransform(cst_PixelRatio, 0, 0, cst_PixelRatio, 0, 0);

            //
            //  Private methods.
            //
            function GetEventOffsetPosition(event) {
                var offset = element.offset();
                return [
                    event.offsetX || event.pageX - offset.left,
                    event.offsetY || event.pageY - offset.top
                ];
            }

            //
            //  Public methods.
            //
            this.GetHTMLElement = function() {
                return element[0];
            };
            this.Invalidate = function() {
                var refresh = false;

                //  Update the position.
                if (last_x != x) {
                    element.css({
                        "left": x
                    });
                    last_x = x;
                    refresh = true;
                }
                if (last_y != y) {
                    element.css({
                        "top": y
                    });
                    last_y = y;
                    refresh = true;
                }

                //  Update the size.
                if (last_width != width) {
                    element.css({
                        "width": width
                    });
                    element[0].width = width * cst_PixelRatio;
                    last_width = width;
                    refresh = true;
                }
                if (last_height != height) {
                    element.css({
                        "height": height
                    });
                    element[0].height = height * cst_PixelRatio;
                    last_height = height;
                    refresh = true;
                }

                //  Update the layer.
                if (last_layer != layer) {
                    element.css({
                        "z-index": layer
                    });
                    last_layer = layer;
                    refresh = true;
                }

                //  Update the visibility.
                if (last_visibility != visibility) {
                    if (visibility == true) {
                        element.show();
                    } else {
                        element.hide();
                    }
                    last_visibility = visibility;
                    refresh = true;
                }

                //  Update the state.
                if (last_state != state) {
                    last_state = state;
                    refresh = true;
                }

                //  Update the availability.
                if (last_enabled != enabled) {
                    last_enabled = enabled;
                    refresh = true;
                }

                //  Update the renderer.
                if (last_renderer != renderer) {
                    last_renderer = renderer;
                    refresh = true;
                }

                //  Render.
                if (refresh == true) {
                    renderer.call(this, context, state);
                }
            };
            this.AccessPosition = Polymorphic.Create(
                [],
                function() {
                    return [x, y];
                },
                [Number, Number],
                function(new_x, new_y) {
                    last_x = x;
                    last_y = y;
                    x = new_x;
                    y = new_y;
                    this.Invalidate();
                }
            );
            this.AccessSize = Polymorphic.Create(
                [],
                function() {
                    return [width, height];
                },
                [Number, Number],
                function(new_width, new_height) {
                    last_width = width;
                    last_height = height;
                    width = new_width;
                    height = new_height;
                    this.Invalidate();
                }
            );
            this.AccessLayer = Polymorphic.Create(
                [],
                function() {
                    return layer;
                },
                [Number],
                function(new_layer) {
                    last_layer = layer;
                    layer = new_layer;
                    this.Invalidate();
                }
            );
            this.AccessVisibility = Polymorphic.Create(
                [],
                function() {
                    return visibility;
                },
                [Boolean],
                function(new_visibility) {
                    last_visibility = visibility;
                    visibility = new_visibility;
                    this.Invalidate();
                }
            );
            this.AccessEnabled = Polymorphic.Create(
                [],
                function() {
                    return enabled;
                },
                [Boolean],
                function(new_enabled) {
                    last_enabled = enabled;
                    enabled = new_enabled;
                    this.Invalidate();
                }
            );
            this.AccessRenderer = Polymorphic.Create(
                [],
                function() {
                    return renderer;
                },
                [Function],
                function(new_renderer) {
                    last_renderer = renderer;
                    renderer = new_renderer;
                    this.Invalidate();
                }
            );
            this.AccessContext = Polymorphic.Create(
                [],
                function() {
                    return context;
                }
            );

            //  Save the "this" pointer.
            var out = this;

            //  Bind events.
            element.mousedown(function(event) {
                state = cst_StateDown;
                out.Invalidate();
                if (enabled == true) {
                    out.TriggerEvent("MouseDown", GetEventOffsetPosition(event));
                }
            }).mousemove(function(event) {
                if (state == cst_StateUp) {
                    state = cst_StateOver;
                    out.Invalidate();
                }
                if (enabled == true) {
                    out.TriggerEvent("MouseMove", GetEventOffsetPosition(event));
                }
            }).mouseup(function(event) {
                state = cst_StateOver;
                out.Invalidate();
                if (enabled == true) {
                    out.TriggerEvent("MouseUp", GetEventOffsetPosition(event));
                }
            }).mouseleave(function(event) {
                state = cst_StateUp;
                out.Invalidate();
                if (enabled == true) {
                    out.TriggerEvent("MouseLeave", GetEventOffsetPosition(event));
                }
            }).click(function(event) {
                if (enabled == true) {
                    out.TriggerEvent("Click", GetEventOffsetPosition(event));
                }
            });

            //  Invalidate immediately after created.
            this.Invalidate();
        }
    );

    //  Inherit "CanvasButton" from "BaseObject".
    Inherit(CanvasButton, ns.BaseObject);

    //  Set the constant members of the "CanvasButton".
    CanvasButton.STATE_UP = cst_StateUp;
    CanvasButton.STATE_OVER = cst_StateOver;
    CanvasButton.STATE_DOWN = cst_StateDown;

    //  Export public APIs / classes..
    Namespace.Export(ns, "CanvasButton", CanvasButton);
});
